package com.candoit.webapps.pricing.bean;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.stereotype.Component;

import com.candoit.webapps.exceptions.SinConsumoPapelMesException;
import com.candoit.webapps.pricing.model.Articulo;
import com.candoit.webapps.pricing.model.Categoria;
import com.candoit.webapps.pricing.model.CentroCostos;
import com.candoit.webapps.pricing.model.CentroCostosCriterio;
import com.candoit.webapps.pricing.model.ConsumoPapelMes;
import com.candoit.webapps.pricing.model.CriterioDistribucion;
import com.candoit.webapps.pricing.model.Cuenta;
import com.candoit.webapps.pricing.model.CuentaMes;
import com.candoit.webapps.pricing.model.Linea;
import com.candoit.webapps.pricing.model.Maquina;
import com.candoit.webapps.pricing.model.MateriaPrima;
import com.candoit.webapps.pricing.model.MateriaPrimaArticulo;
import com.candoit.webapps.pricing.model.Moneda;
import com.candoit.webapps.pricing.model.OrdenTrabajo;
import com.candoit.webapps.pricing.model.TipoLinea;
import com.candoit.webapps.pricing.model.TipoMateriaPrima;
import com.candoit.webapps.pricing.util.QueryUtil;

@Component("pricingService")
public class PricingService {

	private static PricingService instance= null;
	
	private Moneda moneda;
	private Map<CriterioDistribucion, Map<CentroCostos,Double>> matriz_proporcion_criterio_cc;
	private Double gastoImpresionInd = 0.0d;
	private Double gastoImpresionArt = 0.0d;
	private Double gastoConfeccion = 0.0d;
	private Double gastoEmpaquetado = 0.0d;
	private Double gastoTroquelado = 0.0d;
	private Double gastoGuillotinado = 0.0d;

	private Double gastoImpresionIndSim = 0.0d;
	private Double gastoImpresionArtSim = 0.0d;
	private Double gastoConfeccionSim = 0.0d;
	private Double gastoEmpaquetadoSim = 0.0d;
	private Double gastoTroqueladoSim = 0.0d;
	private Double gastoGuillotinadoSim = 0.0d;

	private Double salarioImpresionInd = 0.0d;
	private Double salarioImpresionArt = 0.0d;
	private Double salarioConfeccion = 0.0d;
	private Double salarioEmpaquetado = 0.0d;
	private Double salarioTroquelado = 0.0d;
	private Double salarioGuillotinado = 0.0d;
	
	private Double salarioImpresionIndSim = 0.0d;
	private Double salarioImpresionArtSim = 0.0d;
	private Double salarioConfeccionSim = 0.0d;
	private Double salarioEmpaquetadoSim = 0.0d;
	private Double salarioTroqueladoSim = 0.0d;
	private Double salarioGuillotinadoSim = 0.0d;
	
	
	
	private Integer nivelDelNivelProductivo = 1;
	private Map<TipoLinea,Map<Linea,Map<Integer,Double>>> nivelesProductivos;
	
	public static PricingService getInstance(){
		if (instance == null){
			instance = new PricingService();
		}
		
		return instance;
	}
	
	private PricingService(){
		moneda = QueryUtil.getInstance().getCotizacionMoneda();
		init_matriz_proporcion_criterio_cc();
		initGastoDirecto();
		nivelesProductivos = inicializarNivelesProductivos();
		initSalarioDirecto();
	}
	
	private void initSalarioDirecto(){
		initSalarioDirectoIndustriales();
		initSalarioDirectoArtesanales();
	}
	
	private void initSalarioDirectoArtesanales(){
		Map <CentroCostos,Double> salariosTotalesCCPPD = salariosTotalesCCPPD(false);
		Map <CentroCostos,Double> salariosTotalesCCPPDSim = salariosTotalesCCPPD(true);
		this.salarioImpresionArt = salarioTareaMaquina(CentroCostos.IMPRESORA,salariosTotalesCCPPD, false);
		this.salarioTroquelado = salarioTareaMaquina(CentroCostos.TROQUELADO,salariosTotalesCCPPD, false);
		this.salarioGuillotinado = salarioTareaMaquina(CentroCostos.GUILLOTINA,salariosTotalesCCPPD, false);
		
		this.salarioImpresionArtSim = salarioTareaMaquina(CentroCostos.IMPRESORA,salariosTotalesCCPPDSim, false);
		this.salarioTroqueladoSim = salarioTareaMaquina(CentroCostos.TROQUELADO,salariosTotalesCCPPDSim, false);
		this.salarioGuillotinadoSim = salarioTareaMaquina(CentroCostos.GUILLOTINA,salariosTotalesCCPPDSim, false);
	}
	
	private void initSalarioDirectoIndustriales(){
		Map <CentroCostos,Double> salariosTotalesCCPPD = salariosTotalesCCPPD(false);
		Map <CentroCostos,Double> salariosTotalesCCPPDSim = salariosTotalesCCPPD(true);
		this.salarioImpresionInd = salarioTareaMaquina(CentroCostos.UTECO_CODE,salariosTotalesCCPPD, false);
		this.salarioConfeccion = salarioTareaMaquina(CentroCostos.NEWLONG_CODE,salariosTotalesCCPPD, false);
		this.salarioEmpaquetado = salarioTareaMaquina(CentroCostos.EMPAQUETADORA_CODE,salariosTotalesCCPPD, false);
		
		this.salarioImpresionIndSim = salarioTareaMaquina(CentroCostos.UTECO_CODE,salariosTotalesCCPPDSim, true);
		this.salarioConfeccionSim = salarioTareaMaquina(CentroCostos.NEWLONG_CODE,salariosTotalesCCPPDSim, true);
		this.salarioEmpaquetadoSim = salarioTareaMaquina(CentroCostos.EMPAQUETADORA_CODE,salariosTotalesCCPPDSim, true);
	}
	
	private Double salarioMensualMaquina(Integer maquina, Map <CentroCostos,Double> costosTotalesCCPPD,boolean simulado){		
		CentroCostos cc = QueryUtil.getInstance().getCentroById(maquina);
		Double costoTotalCCPporMaquina = QueryUtil.getInstance().getSalarioByCentroCosto(cc, simulado);
		
		Double costosTotalesCCPPDMaquina = costosTotalesCCPPDMaquina(cc,costosTotalesCCPPD);
		
		Double costoMensualMaquina = costoTotalCCPporMaquina + costosTotalesCCPPDMaquina;
		return costoMensualMaquina;
	}

	private Double salarioTareaMaquina(Integer maquina,Map <CentroCostos,Double> costosTotalesCCPPD, boolean simulado){
		Double costoMensualMaquina = salarioMensualMaquina(maquina,costosTotalesCCPPD,simulado);
		if (maquina == CentroCostos.UTECO_CODE ){
			costoMensualMaquina += salarioMensualMaquina(CentroCostos.CORTE_DE_BOBINA_A_BOBINA_CODE,costosTotalesCCPPD,simulado);
		}else if(maquina == CentroCostos.IMPRESORA){
			costoMensualMaquina += salarioMensualMaquina(CentroCostos.CORTE_DE_BOBINA_A_PLIEGO_CODE,costosTotalesCCPPD,simulado)/2;
		}
		CentroCostos cc = QueryUtil.getInstance().getCentroById(maquina);
		
		Maquina m = QueryUtil.getInstance().getMaquinaByCentroCostos(cc);
		Integer cantidadMaquinas = m.getCantidad();
		
		Double produccionMensualPromedio = m.getProduccionMensual();
		Integer horasMaquinaMensual = m.getHorasMes();
		Double unidadesMaquinasMensuales = produccionMensualPromedio/horasMaquinaMensual;
		
		return (costoMensualMaquina/cantidadMaquinas)/horasMaquinaMensual/unidadesMaquinasMensuales;
	}
	
	private Map <CentroCostos,Double> salariosTotalesCCPPD(boolean simulado){
		return salariosTotalesCCPP(Categoria.SERVICES_CODE, simulado);
	}
	
	private Map <CentroCostos,Double> salariosTotalesCCPP(Integer categoria, boolean simulado){
		Map <CentroCostos,Double> costosTotalesCCPP = new HashMap<CentroCostos, Double>();
		
		List<CentroCostos> paraProductivos = QueryUtil.getInstance().getCentroCostosByCategoria(categoria); 
		
		/*inicializo el mapa*/
		for(CentroCostos cc : paraProductivos){
			costosTotalesCCPP.put(cc, 0.0d);
		}
		
		for(CentroCostos cc : paraProductivos){
			Double totalCuenta = QueryUtil.getInstance().getSalarioByCentroCosto(cc, simulado);
			costosTotalesCCPP.put(cc,totalCuenta);
		}
		
		return costosTotalesCCPP;
	}
	
	private Map<TipoLinea,Map<Linea,Map<Integer,Double>>> inicializarNivelesProductivos(){
		Map<TipoLinea,Map<Linea,Map<Integer,Double>>> np = new HashMap<TipoLinea, Map<Linea,Map<Integer,Double>>>();

		TipoLinea industrial = QueryUtil.getInstance().getTipoLineaById(TipoLinea.INDUSTRIAL);
		np.put(industrial, inicializarNivelesProductivosIndustriales());
		
		TipoLinea artesanal = QueryUtil.getInstance().getTipoLineaById(TipoLinea.ARTESANAL);
		np.put(artesanal, inicializarNivelesProductivosArtesanales());
		
		return np;
	}
	
	private Map<Linea,Map<Integer,Double>> inicializarNivelesProductivosArtesanales(){
		Map<Linea,Map<Integer,Double>> art = new HashMap<Linea, Map<Integer,Double>>();
		List<Linea> lineas = QueryUtil.getInstance().getLineasArtesanales();
		
		List<Double> v1 = new ArrayList<Double>();
		v1.add(150000.0);	v1.add(155000.0);
		
		for (Linea l : lineas){
			Map<Integer,Double> maux = inicializarMapaSegundoNivel(v1);
			art.put( l, maux );
		}
		
		return art;
	}

	private Map<Linea,Map<Integer,Double>> inicializarNivelesProductivosIndustriales(){
		Map<Linea,Map<Integer,Double>> ind = new HashMap<Linea, Map<Integer,Double>>();
		List<Linea> lineas = QueryUtil.getInstance().getLineasIndustriales();
		
		List<Double> v1 = new ArrayList<Double>();
		v1.add(2550000.0);	v1.add(2600000.0); v1.add(2650000.0); v1.add(2700000.0); v1.add(2750000.0);
		
		List<Double> v2 = new ArrayList<Double>();
		v2.add(3000000.0);	v2.add(3050000.0); v2.add(3100000.0); v2.add(3150000.0); v2.add(3200000.0);
		
		for (Linea l : lineas){
			if(l.getId() == Linea.LUNA){
				Map<Integer,Double> maux = inicializarMapaSegundoNivel(v1);
				ind.put( l, maux );
			}else{
				Map<Integer,Double> maux = inicializarMapaSegundoNivel(v2);
				ind.put( l, maux );
			}
		}
		
		return ind;
	}
	
	private Map<Integer,Double> inicializarMapaSegundoNivel(List<Double> valores){
		Map<Integer,Double> maux = new HashMap<Integer,Double>();
		for(Integer i = 1; i<=valores.size() ; i++){
			maux.put(i, valores.get(i-1));
		}
		return maux;
	}
	
	private void initGastoDirecto(){
		initGastoDirectoIndustrial();
		initGastoDirectoArtesanal();
	}
	
	private void init_matriz_proporcion_criterio_cc(){
		List<CriterioDistribucion> criterios = QueryUtil.getInstance().getCriterios();
		List<CentroCostos> ccs = QueryUtil.getInstance().getCentroCostos();
		
		matriz_proporcion_criterio_cc = new HashMap<CriterioDistribucion, Map<CentroCostos,Double>>();
		
		for (CriterioDistribucion c : criterios){
			matriz_proporcion_criterio_cc.put(c,new HashMap<CentroCostos, Double>());
			
			Double sumaProporciones = 0.0d;
			/*Guardo las proporciones no porcentuales en la matriz*/
			for (CentroCostos cc : ccs){
				CentroCostosCriterio ccc = QueryUtil.getInstance().getCentroCostosCriterioObject(cc, c);
				Double propocionNoPorcentual = (ccc == null)? 0.0d : ccc.getCantidad();
				matriz_proporcion_criterio_cc.get(c).put(cc, propocionNoPorcentual);
				sumaProporciones += propocionNoPorcentual;
			}
			
			/*Ahora que conozco el total cargo la proporciones porcentuales en la matriz*/
			for (CentroCostos cc : ccs){
				Double propocionPorcentual = matriz_proporcion_criterio_cc.get(c).get(cc) / sumaProporciones;
				matriz_proporcion_criterio_cc.get(c).put(cc, propocionPorcentual);
			}
		}
	}
	
	protected void initGastoDirectoIndustrial(){
		Map <CentroCostos,Double> costosTotalesCCPPD = costosTotalesCCPPD(false);
		Map <CentroCostos,Double> costosTotalesCCPPDSim = costosTotalesCCPPD(true);
		this.gastoImpresionInd =  gastoTareaMaquina(CentroCostos.UTECO_CODE,costosTotalesCCPPD,false);
		this.gastoConfeccion = gastoTareaMaquina(CentroCostos.NEWLONG_CODE,costosTotalesCCPPD,false);
		this.gastoEmpaquetado = gastoTareaMaquina(CentroCostos.EMPAQUETADORA_CODE,costosTotalesCCPPD,false);
		
		this.gastoImpresionIndSim =  gastoTareaMaquina(CentroCostos.UTECO_CODE,costosTotalesCCPPDSim,true);
		this.gastoConfeccionSim = gastoTareaMaquina(CentroCostos.NEWLONG_CODE,costosTotalesCCPPDSim,true);
		this.gastoEmpaquetadoSim = gastoTareaMaquina(CentroCostos.EMPAQUETADORA_CODE,costosTotalesCCPPDSim,true);
	}
	
	protected void initGastoDirectoArtesanal(){
		Map <CentroCostos,Double> costosTotalesCCPPD = costosTotalesCCPPD(false);
		Map <CentroCostos,Double> costosTotalesCCPPDSim = costosTotalesCCPPD(true);
		this.gastoImpresionArt =  gastoTareaMaquina(CentroCostos.IMPRESORA,costosTotalesCCPPD,false);
		this.gastoTroquelado = gastoTareaMaquina(CentroCostos.TROQUELADO,costosTotalesCCPPD,false);
		this.gastoGuillotinado = gastoTareaMaquina(CentroCostos.GUILLOTINA,costosTotalesCCPPD,false);
		
		this.gastoImpresionArtSim =  gastoTareaMaquina(CentroCostos.IMPRESORA,costosTotalesCCPPDSim,true);
		this.gastoTroqueladoSim = gastoTareaMaquina(CentroCostos.TROQUELADO,costosTotalesCCPPDSim,true);
		this.gastoGuillotinadoSim = gastoTareaMaquina(CentroCostos.GUILLOTINA,costosTotalesCCPPDSim,true);
	}
	
	private Double costoTotalCCPporMaquina(Integer maquina, boolean simulado){
		CentroCostos cc = QueryUtil.getInstance().getCentroById(maquina);
		List<Cuenta> cuentas = QueryUtil.getInstance().getCuentas();
		
		Double costoTotal = 0.0d;
		
		for (Cuenta cu : cuentas){
			CuentaMes cum = QueryUtil.getInstance().getUltimaCuenta(cu);
			Double totalCuenta  =0.00d;
			if (cum != null){
				totalCuenta = simulado? cum.getCostoSimulado() : cum.getCosto();
			}
			CriterioDistribucion c = cu.getCriterio();
			
			Double proporcion = getProporcion(c, cc);
			costoTotal += totalCuenta*proporcion;
		}
		return costoTotal;
	}
	
	private Double getProporcion(CriterioDistribucion c, CentroCostos cc){
		Double proporcion = 0.0d;
		try {
			proporcion = matriz_proporcion_criterio_cc.get(c).get(cc);
		} catch (Exception e) {
		}
		return proporcion;
	}
	
	private Double costoMensualMaquina(Integer maquina, Map <CentroCostos,Double> costosTotalesCCPPD, boolean simulado){		
		CentroCostos cc = QueryUtil.getInstance().getCentroById(maquina);
		Double costoTotalCCPporMaquina = costoTotalCCPporMaquina(maquina, simulado);
		
		Double costosTotalesCCPPDMaquina = costosTotalesCCPPDMaquina(cc,costosTotalesCCPPD);
		
		Double costoMensualMaquina = costoTotalCCPporMaquina + costosTotalesCCPPDMaquina;
		return costoMensualMaquina;
	}
	
	/*No requiere simulacion porque los datos del mapa ya pasaron por ese criterio*/
	private Double costosTotalesCCPPDMaquina(CentroCostos maquina, Map <CentroCostos,Double> costosTotalesCCPPD){		
		Double costoTotal = 0.0d;
		
		for (CentroCostos cc : costosTotalesCCPPD.keySet()){
			Double totalCuenta = costosTotalesCCPPD.get(cc);
			CriterioDistribucion c = cc.getCriterio();
			
			Double proporcion = getProporcion(c, maquina);
			costoTotal += totalCuenta*proporcion;
		}
		return costoTotal;
	}
	
	private Double gastoTareaMaquina(Integer maquina,Map <CentroCostos,Double> costosTotalesCCPPD, boolean simulado){
		Double costoMensualMaquina = costoMensualMaquina(maquina,costosTotalesCCPPD, simulado);
		if( maquina == CentroCostos.UTECO_CODE){
			Double gastoCorteBoB = costoMensualMaquina(CentroCostos.CORTE_DE_BOBINA_A_BOBINA_CODE,costosTotalesCCPPD, simulado);
			costoMensualMaquina += gastoCorteBoB;
		}
		
		CentroCostos cc = QueryUtil.getInstance().getCentroById(maquina);
		
		Maquina m = QueryUtil.getInstance().getMaquinaByCentroCostos(cc);
		Integer cantidadMaquinas = m.getCantidad();
		
		Double produccionMensualPromedio = m.getProduccionMensual();
		Integer horasMaquinaMensual = m.getHorasMes();
		Double unidadesMaquinasMensuales = produccionMensualPromedio/horasMaquinaMensual;
		
		return (costoMensualMaquina/cantidadMaquinas)/horasMaquinaMensual/unidadesMaquinasMensuales;
	}
	
	private Map <CentroCostos,Double> costosTotalesCCPPD(boolean simulado){
		return costosTotalesCCPP(Categoria.SERVICES_CODE, simulado);
	}
	
	private Map <CentroCostos,Double> costosTotalesCCPP(Integer categoria, boolean simulado){
		Map <CentroCostos,Double> costosTotalesCCPP = new HashMap<CentroCostos, Double>();
		
		List<Cuenta> cuentas = QueryUtil.getInstance().getCuentas();
		List<CentroCostos> paraProductivos = QueryUtil.getInstance().getCentroCostosByCategoria(categoria); 
		
		/*inicializo el mapa*/
		for(CentroCostos cc : paraProductivos){
			costosTotalesCCPP.put(cc, 0.0d);
		}
		
		for (Cuenta cu : cuentas){
			CuentaMes cum = QueryUtil.getInstance().getUltimaCuenta(cu);
			if (cum != null){	
				Double totalCuenta = simulado? cum.getCostoSimulado() : cum.getCosto();
				CriterioDistribucion c = cu.getCriterio();
			
				for(CentroCostos cc : paraProductivos){
					Double proporcion = getProporcion(c, cc);
					costosTotalesCCPP.put(cc, costosTotalesCCPP.get(cc) + totalCuenta*proporcion);
				}
			}	
		}
		return costosTotalesCCPP;
	}
	
	private Double getCostoDirectoDolar(Articulo articulo, boolean simulado, TipoLinea tipo){
		return getCostoDirecto(articulo, simulado, tipo)/moneda.getCotizacion();
	}
	
	private Double getCostoDirecto(Articulo articulo, boolean simulado, TipoLinea tipo){
		if(TipoLinea.INDUSTRIAL == tipo.getId()){
			return getCostoDirectoIndustrial(articulo, simulado);
		}else{
			return getCostoDirectoArtesanal(articulo, simulado);
		}
	}
	
	private Double getCostoDirectoArtesanal(Articulo articulo, boolean simulado){
		Double consumoImpresora = calcularConsumoMaquina(TipoMateriaPrima.MO_IMPRESION, articulo);
		Double consumoTroquelado = calcularConsumoMaquina(TipoMateriaPrima.MO_TROQUELADO, articulo);
		Double consumoGuillotinado = calcularConsumoMaquina(TipoMateriaPrima.MO_GUILLOTINA, articulo);
		
		Double costo;
		if (!simulado){
			costo = this.gastoImpresionArt * consumoImpresora + this.gastoTroquelado * consumoTroquelado + 
			this.gastoGuillotinado * consumoGuillotinado;
		}else{
			costo = this.gastoImpresionArtSim * consumoImpresora + this.gastoTroqueladoSim * consumoTroquelado + 
			this.gastoGuillotinadoSim * consumoGuillotinado;
		}
		
		return costo;
	}

	
	private Double getCostoDirectoIndustrial(Articulo articulo, boolean simulado){
		Double consumoUteco = calcularConsumoMaquina(TipoMateriaPrima.MO_IMPRESION, articulo);
		Double consumoConfeccion = calcularConsumoMaquina(TipoMateriaPrima.MO_CONFECCION, articulo);
		Double consumoEmpaquetado = calcularConsumoMaquina(TipoMateriaPrima.MO_EMPAQUE, articulo);
		
		Double costo;
		if (!simulado){
			costo = this.gastoImpresionInd * consumoUteco + this.gastoConfeccion * consumoConfeccion + 
			this.gastoEmpaquetado * consumoEmpaquetado;
		}else{
			costo = this.gastoImpresionIndSim * consumoUteco + this.gastoConfeccionSim * consumoConfeccion + 
			this.gastoEmpaquetadoSim * consumoEmpaquetado;
		}
		
		return costo;
	}
	
	private Double calcularConsumoMaquina(Integer tipoMateriaPrimeID, Articulo articulo){
		TipoMateriaPrima tmp = QueryUtil.getInstance().getTipoMateriaPrimaById(tipoMateriaPrimeID);
		Double consumo = QueryUtil.getInstance().getMateriaPrimaArticulo(articulo, tmp).getConsummo();
		return consumo;
	}
	
	public Double gastoPromedioEstampadora(){
		Map <CentroCostos,Double> costosTotalesCCPPD = costosTotalesCCPPD(false);
		Double costoMensualMaquina = costoMensualMaquina(CentroCostos.ESTAMPADORA,costosTotalesCCPPD, false);
		CentroCostos cc = QueryUtil.getInstance().getCentroById(CentroCostos.ESTAMPADORA);
		
		Maquina m = QueryUtil.getInstance().getMaquinaByCentroCostos(cc);
		Integer cantidadMaquinas = m.getCantidad();
		
		Double gasto = costoMensualMaquina/600/600/cantidadMaquinas;
		return gasto;
	}

	
	private Double calcularCostoMPArticulo(Articulo articulo, MateriaPrima materia, boolean simulado){
		Double costo = 0.0;
		TipoLinea tipoProductivo = getTipoProductivo(articulo);
		List<TipoMateriaPrima> tipos = QueryUtil.getInstance().getTipoMateriaPrimaExceptsMOByTipo(tipoProductivo);
		for (TipoMateriaPrima tipo : tipos){
			MateriaPrimaArticulo consumo = QueryUtil.getInstance().getMateriaPrimaArticulo(articulo, tipo);
			Double cUnit = 0.0;
			if (consumo != null){	
				if (	
						materia.getGramaje() != null
					&& 
						(tipo.getId() == TipoMateriaPrima.PAPEL || tipo.getId() == TipoMateriaPrima.CARTULINA)
					){
					cUnit = costoPapel(materia,simulado)*materia.getGramaje();
				}else if( tipo.getId() == TipoMateriaPrima.MODULO ){//TODO: siempre es el precio del kraft de 100g
					cUnit = costoPapel(materia,simulado);
				}else if(tipo.getId() == TipoMateriaPrima.FONDO){
					cUnit = costoUnitarioNoPapel(tipo,simulado)*materia.getGramaje();
				}else{
					cUnit = costoUnitarioNoPapel(tipo,simulado);
				}
				if (consumo.getConsummo() !=  null){
					costo += consumo.getConsummo() * cUnit;;
				}
			}
		}
		
		return costo;
	}
	
	private Double costoUnitarioNoPapel(TipoMateriaPrima tipo, boolean simulado){
		Double costoUnitario = 0.0;
		MateriaPrima mp = QueryUtil.getInstance().getMateriaPrimaByTipoMateria(tipo).get(0);
		mp.setCostoUnitario( (mp.getCostoUnitario() != null)? mp.getCostoUnitario() : 0.0 );
		if (!simulado){	
			costoUnitario = mp.getCostoUnitario();
		}else{
			if (mp.getCostoSimulado()!= null && mp.getCostoSimulado() != 0.0){
				costoUnitario = mp.getCostoSimulado();
			}else{
				costoUnitario = mp.getCostoUnitario();
			}
		}
		return costoUnitario;
	}
	
	private Double costoPapel(MateriaPrima materia, boolean simulado){
		Double costoUnitario = 0.0;
		if (!simulado){	
			costoUnitario = materia.getCostoUnitario();
		}else{
			if (materia.getCostoSimulado()!= null){
				costoUnitario = materia.getCostoSimulado();
			}else{
				costoUnitario = materia.getCostoUnitario();
			}
		}
		return costoUnitario;
	}
	
	private TipoLinea getTipoProductivo(Articulo articulo){
		return articulo.getFormato().getLinea().getTipoLinea();
	}
	
	private Double getCostoIndirecto(Double nivelProductivo, boolean simulado){
		Double gastoSinAsignarIndustriales = gastoSinAsignarIndustriales(simulado);
		return calculoInternoIndirecto(nivelProductivo, gastoSinAsignarIndustriales);
	}
	
	private Double calculoInternoIndirecto(Double nivelProductivo, Double sinAsignarIndustriales){
		CentroCostos cc = QueryUtil.getInstance().getCentroById(CentroCostos.NEWLONG_CODE);
		Maquina m = QueryUtil.getInstance().getMaquinaByCentroCostos(cc);
		Integer cantidadNewlong = m.getCantidad();
		Double indirecto = (sinAsignarIndustriales/cantidadNewlong)/nivelProductivo; 
		return indirecto;
	}
	
	private Double gastoSinAsignarIndustriales(boolean simulado){
		Map <CentroCostos,Double> costosTotalesCCPPI = costosTotalesCCPPI(simulado);
		return calculoInternoSinAsignar(costosTotalesCCPPI);
	}
	
	private Double calcularPorcOTIndustrial(){
		List<OrdenTrabajo> otm = QueryUtil.getInstance().getOrdenesTrabajo();
		Double OTTotal = 0.0;
		Double OTIndustrial = 0.0;
		for (OrdenTrabajo ot : otm){
			if (ot.getId() == OrdenTrabajo.AUTOMATICA_CODE){
				OTIndustrial = new Double(ot.getCantidad());
			}
			OTTotal += ot.getCantidad();
		}
		OTIndustrial = OTIndustrial/OTTotal;
		
		return OTIndustrial;
	}
	
	private Double calcularPorcKgIndustriales(){
		ConsumoPapelMes cpm = QueryUtil.getInstance().getConsumoPapelMes(OrdenTrabajo.AUTOMATICA_CODE);
		Double mixConsumoPapel = cpm.getMixConsumoPapel();
		Double mixOrdenesTrabajoEmitidas = cpm.getMixOrdenesTrabajoEmitidas();
		Double porcConsumoPapel = 100 * calcularPorcConsumoPapelIndustrial();
		Double industrialMCP = mixConsumoPapel * porcConsumoPapel;
		Double porcOTI = 100 * calcularPorcOTIndustrial();
		Double industrialMOTE = mixOrdenesTrabajoEmitidas * porcOTI;
		Double porcKGI = (industrialMCP + industrialMOTE)/100;
		return porcKGI;
	}
	
	private Double calcularPorcConsumoPapelIndustrial(){
		List<ConsumoPapelMes> cpm = QueryUtil.getInstance().getConsumoPapelMes();
		Double CTotal = 0.0;
		Double CIndustrial = 0.0;
		for (ConsumoPapelMes cp : cpm){
			if (cp.getOrden().getId() == OrdenTrabajo.AUTOMATICA_CODE){
				CIndustrial = cp.getKilos();
			}
			CTotal += cp.getKilos();
		}
		CIndustrial = CIndustrial/CTotal;
		
		return CIndustrial;
	}
	
	private Double calculoInternoSinAsignar(Map <CentroCostos,Double> valoresTotalesCCPPI){
		Double OTIndustrial = calcularPorcOTIndustrial();
		Double KgIndustriales = calcularPorcKgIndustriales();
		Double costoSinExpedicion = 0.0;
		Double costoConExpedicion = 0.0;
		for(CentroCostos cc : valoresTotalesCCPPI.keySet()){
			if (cc.getId() != 29){
				costoSinExpedicion += valoresTotalesCCPPI.get(cc);
			}else{
				costoConExpedicion = valoresTotalesCCPPI.get(cc);
			}
		}
		
		Double gastoSinAsignarIndustriales = 0.0;
		gastoSinAsignarIndustriales = costoSinExpedicion * OTIndustrial + costoConExpedicion * KgIndustriales;
		return gastoSinAsignarIndustriales;
	}
	
	public Integer getNivelDelNivelProductivo(){
		if(this.nivelDelNivelProductivo == null){
			this.nivelDelNivelProductivo = 1;
		}
		return this.nivelDelNivelProductivo;
	}
	
	private Map <CentroCostos,Double> costosTotalesCCPPI(boolean simulado){
		return costosTotalesCCPP(Categoria.SIN_ASIGNAR_CODE, simulado);
	}
	
	public Double getNivelProductivo(Linea linea, Integer nivel, TipoLinea tipo){
		return nivelesProductivos.get(tipo).get(linea).get(nivel);
	}
	
	private Double getCostoSalarios(Articulo articulo, boolean simulado) throws SinConsumoPapelMesException{
		Double costo;
		Double salarioIndirecto = getSalarioIndirecto(articulo,simulado);
		if (!simulado){
			costo = getSalarioDirecto(articulo, simulado) + salarioIndirecto;
		}else{
			costo = getSalarioDirecto(articulo, simulado) + salarioIndirecto;
		}
		
		return costo;
	}
	
	private Double getSalarioIndirecto(Articulo articulo,boolean simulado) throws SinConsumoPapelMesException{
		Integer nivel = getNivelDelNivelProductivo();
		TipoLinea tipoProductivo = getTipoProductivo(articulo);
		Double nivelProductivo = getNivelProductivo(articulo.getFormato().getLinea(), nivel, tipoProductivo);
		Double gastoSinAsignarIndustriales = salariosSinAsignarIndustriales(simulado);
		return calculoInternoIndirecto(nivelProductivo, gastoSinAsignarIndustriales);
	}
	
	private Double salariosSinAsignarIndustriales(boolean simulado) throws SinConsumoPapelMesException{
		List<CentroCostos> ccppi = QueryUtil.getInstance().getCentroCostosByCategoria(Categoria.SIN_ASIGNAR_CODE);
		Map <CentroCostos,Double> salariosTotalesCCPPI = costosTotalesCCPP(Categoria.SIN_ASIGNAR_CODE, simulado);
		for(CentroCostos cc : ccppi){
			Double totalCuenta = QueryUtil.getInstance().getSalarioByCentroCosto(cc, simulado);
			salariosTotalesCCPPI.put(cc,totalCuenta);
		}
		
		return calculoInternoSinAsignar(salariosTotalesCCPPI);
	}
	
	private Double getSalarioDirecto(Articulo articulo, boolean simulado){
		if(TipoLinea.INDUSTRIAL == getTipoProductivo(articulo).getId()){
			return getSalarioDirectoIndustrial(articulo, simulado);
		}else{
			return getSalarioDirectoArtesanal(articulo, simulado);
		}		
	}
	
	private Double getSalarioDirectoArtesanal(Articulo articulo, boolean simulado){
		Double consumoImpresora = calcularConsumoMaquina(TipoMateriaPrima.MO_IMPRESION, articulo);
		Double consumoTroquelado = calcularConsumoMaquina(TipoMateriaPrima.MO_TROQUELADO, articulo);
		Double consumoGuillotinado = calcularConsumoMaquina(TipoMateriaPrima.MO_GUILLOTINA, articulo);
		
		Double costo;
		if (!simulado){
			costo = this.salarioImpresionArt * consumoImpresora + this.salarioTroquelado * consumoTroquelado + 
			this.salarioGuillotinado * consumoGuillotinado;
		}else{
			costo = this.salarioImpresionArtSim * consumoImpresora + this.salarioTroqueladoSim * consumoTroquelado + 
			this.salarioGuillotinadoSim * consumoGuillotinado;			
		}
		
		return costo;
	}
	
	private Double getSalarioDirectoIndustrial(Articulo articulo, boolean simulado){
		Double consumoUteco = calcularConsumoMaquina(TipoMateriaPrima.MO_IMPRESION, articulo);
		Double consumoConfeccion = calcularConsumoMaquina(TipoMateriaPrima.MO_CONFECCION, articulo);
		Double consumoEmpaquetado = calcularConsumoMaquina(TipoMateriaPrima.MO_EMPAQUE, articulo);
		
		Double costo;
		if (!simulado){
			costo = this.salarioImpresionInd * consumoUteco + this.salarioConfeccion * consumoConfeccion + 
			this.salarioEmpaquetado * consumoEmpaquetado;
		}else{
			costo = this.salarioImpresionIndSim * consumoUteco + this.salarioConfeccionSim * consumoConfeccion + 
			this.salarioEmpaquetadoSim * consumoEmpaquetado;			
		}
		
		return costo;
	}
	
	private Double calcularPrecio(Double costo, Double rentabilidad){
		return costo * (1 + rentabilidad/100);
	}
	
	public Double getPrice(Articulo articulo,MateriaPrima materia) throws SinConsumoPapelMesException{
		TipoLinea tipo = getTipoProductivo(articulo);
		Integer nivel = getNivelDelNivelProductivo();
		Double nivelProductivo = getNivelProductivo(articulo.getFormato().getLinea(), nivel, tipo);
		
		Double costoMp = calcularCostoMPArticulo(articulo, materia,false);
		Double scrap = costoMp * 0.10d;
		costoMp = costoMp + scrap;
		
		Double costoDirecto = getCostoDirecto(articulo, false, tipo);
		
		Double costoIndirecto = getCostoIndirecto(nivelProductivo, false);
		
		Double costoSalarios = getCostoSalarios(articulo,false);
		
		Double costoReal= costoMp + costoDirecto + costoIndirecto + costoSalarios;
		
		Double rentabilidad = 0.00d;
		if (articulo.getFormato() != null){
			rentabilidad = articulo.getFormato().getRentabilidad();
		}
		Double precio = calcularPrecio(costoReal, rentabilidad);
		return precio;
	}
}
